<!-- V3版本的el-table组件，支持国际化-->
<template>
  <div class="tableXV3" ref="tableXV3Dom">
    <el-table class="tableXV3Content" ref="tableV3Dom" v-bind="$attrs"
              :data="tableDataD"
              :scrollbar-always-on="scrollAlways" :size="props.size"
              :stripe="props.stripe" :border="props.border"
              :row-key="idKey" :tree-props="treeProps"
              :highlight-current-row="highlightCurrentRow"

              @select="selectFn" @select-all="selectAllFn"


              @expand-change="expandFn"
              :expand-row-keys="nowExpandRowList"

              @header-click="headerClickFn"
              :row-class-name="addRowClassFn"
              :cell-class-name="addCellClassFn"
              :header-cell-class-name="addHeadCClassFn"

              @row-click="rowClickFn" @row-dblclick="rowClickDFn">

      <el-table-column v-for="(item,index) in tableTitleD"
                       :key="`_${item.label}_${item.prop}_${item.slot}`"
                       v-bind="item">

        <!-- region 头部插槽 -->
        <template #header="{column,$index}">
          <div class="v3Label">
            <slot v-if="item.labelSlot" :name="item.labelSlot" :item="item"
                  :column="column" :$index="$index" :no="column.no">
              <span class="labelSlotItem"></span>
            </slot>
            <div class="checkLabel myCheck" v-else-if="item.noClick && item.isSelection">
              <el-checkbox v-model="labelCheck" size="large"
                           :indeterminate="isIndeterminate"
                           @change="labelCheckChangeFn"/>
            </div>

            <!-- region 默认的头部字段值 -->
            <span class="defaultLabel" v-else>
              {{ labelI18Fn(column, $index) }}
              <el-tooltip class="sortLabel" v-if="item.help"
                          v-bind="item.helpOptions"
                          :placement="item.helpOptions?.placement?item.helpOptions?.placement:'top'">
                <span class="labelHelpIcon">
                  <icon-x size="16" type="questionFilled"/>
                </span>
                <template #content>
                  <span>{{ item.help }}</span>
                </template>
              </el-tooltip>
            </span>
            <!-- endregion -->

          </div>
        </template>
        <!-- endregion -->

        <!-- region 自定义勾选 -->
        <template #default="{row,column,$index}" v-if="item.noClick && item.isSelection">
          <div class="checkValue myCheck" @click.stop="">
            <el-checkbox v-model="nowCheckKeyValue[`${row[`${idKey}`]}`]"
                         :disabled="!selectionFilterFn(row,$index)"
                         @change="checkChangeFn($event,row[`${idKey}`])"/>
          </div>
        </template>
        <!-- endregion -->


        <!-- region 内容插槽 -->
        <template v-else-if="!(['selection','index'].includes(item.type))"
                  #default="{row, column, $index}">
          <span class="v3Content">
              <!-- region 自定义插槽 -->
            <span class="slotItem" v-if="item.slot">
              <slot :name="item.slot" :item="item" :row="row"
                    :column="column" :$index="$index"
                    :callBackQuery="props.callBackQuery"/>
            </span>
            <!-- endregion -->

            <!-- region 回调函数 -->
            <span class="callBackItem" v-else-if="typeof item.callBack == 'function'">
              {{ item.callBack(row, column, $index, props.callBackQuery) }}
            </span>

            <span class="htmlItem" v-else-if="typeof item.html == 'function'">
              <span v-html="item.html(row,column,$index,props.callBackQuery)"></span>
            </span>
            <!-- endregion -->

            <span class="defaultItem" v-else>
              {{ item.i18Fn(row, item, 'content') }}
            </span>
          </span>
        </template>
        <!-- endregion -->

      </el-table-column>

      <slot name="otherColumn"/>
    </el-table>
  </div>
</template>

<script setup>
import {ref, nextTick, watch, computed, useAttrs, h} from 'vue'
import IconX from './xhykIcon.vue'
import { ElTable,ElTableColumn,ElCheckbox,ElTooltip } from 'element-plus'


const $attrsObj = useAttrs()
// 获取属性对应值或方法。如果property为方法时，不需要传on前缀。
const getAttrFn = (property = '') => {
  let nowKey = ''
  const newFind = property.toLowerCase().replaceAll('-', '')
  for (let key in $attrsObj) {
    const tempKey = key.toLowerCase().replaceAll('-', '')
    // 查找属性或事件
    if (tempKey == newFind || tempKey == `on${newFind}`) {
      nowKey = key
      break
    }
  }
  if (nowKey) {
    return $attrsObj[nowKey]
  }
}

const props = defineProps({
  // 当前勾选的列表。仅当出现选项时
  modelValue: {
    type: Object,
    default: []
  },

  // 表单数据
  tableData: {
    type: Object,
    default: []
  },

  // 改变的字段，保存在本地
  localField: {
    type: String,
    default: undefined
  },

  // 表单标题字段
  tableTitle: {
    type: Object,
    // slot：自定义插槽名字，数据列表行的自定义内容，里面包含了element组件的三个参数值。
    // show: 是否显示当前列，非false的值都默认显示。
    // callBack: 显示内容前的回调函数。callBack(当前row对象)。返回一个新的显示内容。
    // html: 已html的形式渲染，如：{html: (row,column,$index)=>'<span style="color:red;">12</span>'}。
    // rowClick: 点击单元格是否会触发点击行事件。如果是插槽需要配置slotClick，其他默认是需要。为false时插槽也不会触发事件
    // slotClick: 如果是插槽默认是不需要，true时是触发点击事件。
    // rowSelect: 点击单元格是否会触发行勾选改变（配合selectChange）。如果是插槽默认是不需要，其他默认是需要。
    // noClick: 是否启动自定义勾选,
    // help: string，开启说明提示文本。
    // helpOptions: {}，提示组件的属性。
    default: [
      {label: '序号', prop: 'title', isInput: 'name'},
      {label: '查阅状态', prop: 'phone', slot: 'cy', type: 'index', sortable: true}]
  },

  // 同tableTitle的noClick一样(但是element自带的)，true时（比treeProps优先级高）树形结构是不进行勾选子级。版本2.8.0以上支持
  noClick: {
    type: Boolean,
    default: false
  },

  // tableTitle使用了callBack函数时，额外给回调函数传递的其他参数
  callBackQuery: {
    type: Object,
    default: {}
  },

  // 滚动条设置
  scrollStyle: {
    type: Object,
    default: {
      // always: true, // 可以显示滚动条时，总是显示滚动条
      // width: '14px', // 两条滚动条宽带。接受Number或string
      // widthX: '14px', // 横向滚动条宽带。下面值都有总体和XY区分（XY权限优先）
      // color: '#909399',  // 两条滚动条颜色
      // opacity: 0.7, // 滚动条透明度
      // translate: '2px', // 偏移量
      // show: true,// 不写或true为显示两条滚动条，否则隐藏
    }
  },

  // 国际化函数
  i18Fn: {
    type: Function,
    // row：如果是头部，则为空的{}。tableTitleItem：为字段数据。which：'label'为头部，'content'为内容数据（此时第一个参数为数据row）
    default: (row = {}, tableTitleItem = {}, which = 'content') => {
      if (which == 'label') {
        return tableTitleItem.label
      } else {
        if (tableTitleItem.prop) {
          return row[tableTitleItem.prop]
        } else {
          return ''
        }
      }
    }
  },

  // 斑马线
  stripe: {
    type: Boolean,
    default: true
  },

  // 纵向边框
  border: {
    type: Boolean,
    default: true
  },

  // 表格大小样式
  size: {
    type: String,
    default: 'small'
  },

  // 点击行时，对多选框的操作
  selectChange: {
    type: [Number, String],
    // type： -1：不做任何操作。0：只会取消，1：只会勾选。2：反选。3：清空其他选中，选中当前。4：清空其他选中，反选当前
    default: -1
  },

  // 唯一key。为了防止后端返回的id不是唯一，可以设置多个字段值拼接，如：['id','code'] => `${id}_${code}`
  rowKey: {
    type: [Object, String],
    default: 'id'
  },

  // 行选中时添加的类名，现在已经换成官方回调，旧版本需要配合fatherSelect才更正确
  checkClass: {
    type: [String, Object],
    // 或者是字符串列表
    default: 'tableXV3checkClass'
  },

  // 同页面时多个表格，需要设置父选择进行区分
  fatherSelect: {
    type: String,
    default: ''
  },

  highlightCurrentRow: {
    type: Boolean,
    default: true
  },


  // region 列样式/类名

  // 点击头部时,当前列的单元格类名
  nowColumnClass: {
    // 方法时，接受当前选中列，和当前遍历到的数据列
    type: [String, Function],
    default: 'tableXV3_nowColumn'
  },

  // 是否开启自己类名
  columnColor: {
    type: Boolean,
    default: true
  },

  // endregion


  // region 树形结构

  // 是否解析为树形机构。确定不是树形结构时，设置这个可提高速度
  isTree: {
    type: Boolean,
    default: true
  },

  // 树形结构
  treeProps: {
    type: Object,
    default: {}
  },

  // 树形结构图标
  treeIcon: {
    type: String,
    default: 'CaretRight'
  },

  // endregion

})

// 这个组件的根元素
const tableXV3Dom = ref({})

// 表格组件对象
const tableV3Dom = ref({})

const emit = defineEmits(['update:modelValue', 'cellClick', 'rowClick', 'rowClickD', 'selectX', 'headerClick'])

// region 工具函数

// 获取id
function getMid(len = 30) {
  let result = Date.now().toString();
  while (result.length < len) {
    let temp = Math.random().toString(36)
    if (temp.length > 2) {
      temp = temp.slice(2, temp.length)
    } else {
      temp = "xhyk"
    }
    result = `${temp}${result}`
  }

  if (result.length > len) {
    let index = result.length - len
    result = result.slice(index, result.length)
  }

  return result
}

// 判断prop传参的值。接受[String,Number]的style样式尺寸
function getPx(text, defaultPx = 'px') {
  let tempList = ['px', 'rpx', 'vw', 'vh', '%', 'em', 'auto']
  if (typeof text == 'string') {
    const havePx = tempList.find(row => text.includes(row))
    if (havePx) {
      return text
    } else {
      return `${parseFloat(text)}${defaultPx}`
    }
  } else if (typeof text == 'number') {
    let temp = parseFloat(text)
    return `${temp}${defaultPx}`
  }
  return text
}


// 遍历嵌套取值。根据children字段遍历下级列表。返回当前子级对象
async function findChildren(data = [], callBack = undefined, children = "children") {
  let tempResult = undefined
  const findFn = async (ChildData, father) => {
    let isOk = false
    for (let item of ChildData) {
      if (typeof callBack == "function") {
        const isBreak = await callBack(item, father)
        if (isBreak == true) {
          tempResult = item
          isOk = true
          break;
        }
      }
      if (isOk) {
        tempResult = item
        return item
      }

      // 如果有下一级数据
      if (item[children] != undefined && item[children].length > 0) {
        let result = await findFn(item[children], item)
        if (result) {
          return true
        }
      }
    }
    return isOk;
  }
  await findFn(data, {})
  return tempResult
}

// 字符串或数组去重复元素
function removeSame(Obj) {
  if (typeof Obj === 'string') {
    const text = new Set(Obj)
    return [...text].join('')
  } else if (Obj instanceof Array) {
    return Array.from(new Set(Obj))
  } else {
    return Obj
  }
}

// 防抖/节流类
class MyTimeOut {

  constructor(oneTime = 500, lastTime = 500, beforeTime = 1000) {
    // oneTime：节流延迟，只有第一次有效，常用滚动分页。
    this.oneTime = oneTime
    // lastTime：防抖延迟，只有最后一次有效，常用搜索框
    this.lastTime = lastTime

    this.oneId = undefined
    this.lastId = undefined

    // 先执行，后多个毫秒内不再执行
    this.haveRunBefore = false
    this.beforeTime = beforeTime
    this.nowBeforeIndex = 0
  }

  // 节流执行。只执行第一次
  runOne(callBack) {
    if (this.oneId) {
      return;
    }
    this.oneId = setTimeout(async () => {
      await callBack()
      this.oneId = undefined
    }, this.oneTime)
  }

  // 防抖执行。只执行最后一次
  runLast(callBack) {
    clearTimeout(this.lastId)
    this.lastId = setTimeout(async () => {
      await callBack()
      this.lastId = undefined
    }, this.lastTime)
  }

  // 先行后节流
  async runBefore(callBack) {
    if (this.haveRunBefore || this.nowBeforeIndex == 1) {
      return;
    }
    this.haveRunBefore = true
    if (typeof callBack == 'function') {
      this.nowBeforeIndex = 1
      setTimeout(() => {
        this.nowBeforeIndex = 0
      }, this.beforeTime)
      await callBack()
    }
    this.haveRunBefore = false
  }

}

// endregion


// region 表格方法

// 进行勾选改变。type -1：不做任何操作。0：只会取消，1：只会勾选。2：反选。3：清空其他选中，选中当前。4：清空其他选中，反选当前。
// toEmit：触发勾选事件。needFind：当为对象时，需要内容调用查找。needModel：更新双向绑定
const setChoose = async (rowOrKeyValue, type = 2,
                         toEmit = true, needFind = true,
                         needModel = true) => {
  let thisType = parseInt(type)
  if (thisType == -1) {
    return;
  }

  let isChoose = false

  // region 自定义勾选
  if (haveMySelection.value) {
    let thisKey = rowOrKeyValue
    if (typeof rowOrKeyValue != 'string') {
      thisKey = setKeyValue(rowOrKeyValue)
    }

    switch (thisType) {
      case 0:
        nowCheckKeyValue.value[thisKey] = false
        break;
      case 1:
        nowCheckKeyValue.value[thisKey] = true
        break;
      case 2:
        nowCheckKeyValue.value[thisKey] = !nowCheckKeyValue.value[thisKey]
        break;
      case 3:
        for (let key in nowCheckKeyValue.value) {
          nowCheckKeyValue.value[key] = false
        }
        nowCheckKeyValue.value[thisKey] = true
        break;
      case 4:
        nowCheckKeyValue.value[thisKey] = !nowCheckKeyValue.value[thisKey]
        for (let key in nowCheckKeyValue.value) {
          if (key != thisKey) {
            nowCheckKeyValue.value[key] = false
          }
        }
        break

    }

    getCheckValueFn()
    isChoose = nowCheckKeyValue.value[thisKey]

    let thisChooseItem = {}
    let newChooseList = levelTableData.value.filter(child => {
      if (child[idKey.value] == thisKey) {
        thisChooseItem = child
      }
      return nowCheckKeyValue.value[child[idKey.value]] == true
    })

    if (needModel) {
      await updateDataFn(newChooseList)
    }

    if (toEmit) {
      emit('selectX', {
        list: newChooseList,
        row: thisChooseItem,
        toChoose: isChoose
      })
    }
    return;
  }
  // endregion


  // region 自带勾选
  let thisItem = rowOrKeyValue
  if (typeof rowOrKeyValue == 'string') {
    thisItem = levelTableData.value.find((child) => {
      if (child[idKey.value] == rowOrKeyValue) {
        return true
      }
    })

  } else {

    // 自定义key为空而且需要查找对象时
    if (needFind) {
      setKeyValue(rowOrKeyValue)
      thisItem = levelTableData.value.find((child) => {
        if (child[idKey.value] == rowOrKeyValue[idKey.value]) {
          return true
        }
      })
    }

  }

  // 进行勾选改变
  // TODO: Warn(2024/12/15 17:54 by xhyk)：nextTick()修改回显勾选不及时
  nextTick(async () => {
    if (thisItem) {
      switch (thisType) {
        case 0:
          tableV3Dom.value.toggleRowSelection(thisItem, false)
          break;
        case 1:
          tableV3Dom.value.toggleRowSelection(thisItem, true)
          isChoose = true
          break;
        case 2:
          let tempNowSelectList = tableV3Dom.value.getSelectionRows()
          const haveChoose = tempNowSelectList.find(item => item[idKey.value] == rowOrKeyValue[idKey.value])
          if (haveChoose) {
            tableV3Dom.value.toggleRowSelection(thisItem, false)
          } else {
            tableV3Dom.value.toggleRowSelection(thisItem, true)
            isChoose = true
          }
          break;
        case 3:
          tableV3Dom.value.clearSelection()
          tableV3Dom.value.toggleRowSelection(thisItem, true)
          isChoose = true
          break;
        case 4:
          let tempList = tableV3Dom.value.getSelectionRows()
          const haveChoose2 = tempList.find(item => item[idKey.value] == rowOrKeyValue[idKey.value])
          tableV3Dom.value.clearSelection()
          if (haveChoose2) {
            tableV3Dom.value.toggleRowSelection(thisItem, false)
          } else {
            tableV3Dom.value.toggleRowSelection(thisItem, true)
            isChoose = true
          }
          break
      }

      let tempNowChoose = tableV3Dom.value.getSelectionRows()

      if (needModel) {
        await updateDataFn(tempNowChoose)
      }

      if (toEmit) {
        emit('selectX', {
          list: tempNowChoose,
          row: thisItem,
          toChoose: isChoose
        })
      }

    }
  })
  // endregion

}

// 设置全选择
const setAllChoose = async (toChoose = false) => {
  // 自带的勾选
  if (haveMySelection.value) {
    labelCheckChangeFn(toChoose)
    return;
  }


  if (toChoose == false) {
    tableV3Dom.value.clearSelection()
    await updateDataFn([])
  } else {
    levelTableData.value.forEach(child => {
      setChoose(child, 1, false, false)
    })
    await updateDataFn(levelTableData.value)
  }

  emit('selectX', {
    list: toChoose == true ? levelTableData.value : [],
    row: undefined,
    toChoose: true
  })

}

// 获取当前数据中的选择。注意是只获取当前分页表格数据中的勾选，不是modelValue的全部列表
const getChoose = () => {
  if (haveMySelection.value) {
    let tempList = levelTableData.value.filter(child => {
      return nowCheckKeyValue.value[child[idKey.value]] == true
    })
    return tempList
  }
  return tableV3Dom.value.getSelectionRows()
}

// endregion


// region 表格字段

// 勾选过滤函数
let selectionFilterFn = () => {
  return true
}

// 是否存在自定义勾选选项。为了让自定义勾选不会被自带勾选的层级影响。
const haveMySelection = ref(false)

const tableTitleD = ref([])

const setTitle = () => {
  let result = []
  haveMySelection.value = false
  props.tableTitle?.forEach((item, index) => {

    item.align = item.align == undefined ? 'center' : item.align

    if (item.width != undefined) {
      item.width = getPx(item.width)
    }

    if (item.minWidth != undefined) {
      item.minWidth = getPx(item.minWidth)
    }


    // 不进行选择下级的自定义勾选
    if ((item.type == 'selection' && item.noClick) || item.isSelection) {
      if (typeof item.selectable == 'function') {
        selectionFilterFn = item.selectable
      }
      item.type = 'index'
      item.isSelection = true
      haveMySelection.value = true
    }

    if (item.i18Fn == undefined) {
      item.i18Fn = props.i18Fn
    }

    if (typeof item.fixed != 'string') {
      item.fixed = undefined
    }

    if (item.show != false) {
      result.push(item)
    }
  })
  tableTitleD.value = result
  if (props.localField) {
    localStorage.setItem(props.localField, JSON.stringify(tableTitleD.value))
  }
}

watch(() => props.tableTitle, (newV, oldV) => {
  nextTick(() => {
    setTitle()
  })
}, {immediate: true, deep: true})

// endregion


// region 滚动条样式

const scrollWidthX = ref('')
const scrollWidthY = ref('')
const scrollTranslateX = ref('')
const scrollTranslateY = ref('')
const scrollColorX = ref('')
const scrollColorY = ref('')
const scrollDisplayX = ref('')
const scrollDisplayY = ref('')
const scrollOpacityX = ref(0.7)
const scrollOpacityY = ref(0.7)
const scrollAlways = ref(false)

watch(() => props.scrollStyle, (newV, oldV) => {

  // region 宽度
  let width = newV.width == undefined ? '10px' : newV.width;
  scrollWidthX.value = getPx(width)
  scrollWidthY.value = getPx(width)
  if (newV.widthX != undefined) {
    scrollWidthX.value = getPx(newV.widthX)
  }
  if (newV.widthY != undefined) {
    scrollWidthY.value = getPx(newV.widthY)
  }
  // endregion

  // region 偏移量
  let translate = newV.translate == undefined ? '3px' : newV.translate;
  scrollTranslateX.value = getPx(translate)
  scrollTranslateY.value = getPx(translate)
  if (newV.translateX != undefined) {
    scrollTranslateX.value = getPx(newV.translateX)
  }
  if (newV.translateY != undefined) {
    scrollTranslateY.value = getPx(newV.translateY)
  }

  // endregion

  // region 颜色
  let color = newV.color == undefined ? '#909399' : newV.color
  scrollColorX.value = color
  scrollColorY.value = color
  if (newV.colorX != undefined) {
    scrollColorX.value = newV.colorX
  }
  if (newV.colorY != undefined) {
    scrollColorY.value = newV.colorY
  }
  // endregion

  // region 透明度
  let opacity = newV.opacity == undefined ? 0.7 : newV.opacity
  scrollOpacityX.value = opacity
  scrollOpacityY.value = opacity
  if (newV.opacityX != undefined) {
    scrollOpacityX.value = newV.opacityX
  }
  if (newV.opacityY != undefined) {
    scrollOpacityY.value = newV.opacityY
  }
  // endregion

  // region 显示
  let show = (newV.show == undefined || newV.show == true) ? 'block' : 'none'
  scrollDisplayX.value = show
  scrollDisplayY.value = show
  if (newV.showX != undefined) {
    scrollDisplayX.value = (newV.showX == undefined || newV.showX == true) ? 'block' : 'none'
  }
  if (newV.showY != undefined) {
    scrollDisplayY.value = (newV.showY == undefined || newV.showY == true) ? 'block' : 'none'
  }
  // endregion

  // region 总是显示
  scrollAlways.value = (newV.always == undefined || newV.always == true) ? true : newV.always
  // endregion

}, {immediate: true, deep: true})

// endregion


// region 国际化
// 表头字段的国际化处理
const labelI18Fn = (column, $index) => {
  let no = column.no
  let thisTitle = tableTitleD.value[no]
  return props.i18Fn({}, thisTitle, 'label')
}

// region 例子
// const getTitleFn = (row, titleItem, which) => {
//   if (which == 'label') {
//     if (titleItem.i18Text) {
//       return var_i18n_getText(titleItem.i18Text)
//     }
//     return titleItem.label
//   } else {
//     if (titleItem.prop) {
//       // 数据国际化显示
//       if (row.i18Text && titleItem.prop == 'reqApiType') {
//         return var_i18n_getText(row.i18Text)
//       }
//       return row[titleItem.prop]
//     }
//     return ''
//   }
// }
// endregion

// endregion


// region 树形结构和懒加载
const treeProps = computed(() => {
  let result = {hasChildren: 'hasChildren', children: 'children'}
  Object.assign(result, props.treeProps)
  if (props.noClick == true) {
    result.checkStrictly = true
  }
  return result
})

// 当前折叠
const nowExpandRowList = ref([])

// 当发生张开时
const expandFn = (row, expandedRows) => {
  // console.log(123,row,expandedRows)
  // 因为一开始是nowExpandRowList为空，初始状态是展开时，折叠一个因为空列表而导致全部都折叠
//   if (expandedRows) {
  //   nowExpandRowList.value.push(row[idKey.value])
//   } else {
//     nowExpandRowList.value = nowExpandRowList.value.filter(item => item[idKey.value] != expandedRows[idKey.value])
//   }
//   removeSame(nowExpandRowList.value)
}

// 折叠/展开指定数据。isAll：是否孙级也展开
const setExpand = async (rowList = [], toExpand = true, isAll = true) => {
  nowExpandRowList.value = []
  if (toExpand) {
    if (isAll) {
      // await findChildren(rowList, (child, father) => {
      //   nowExpandRowList.value.push(child[idKey.value])
      //   if (father != undefined) {
      //     nowExpandRowList.value.push(father[idKey.value])
      //   }
      // }, treeProps.value.children || 'children')
      levelTableData.value.forEach(row => nowExpandRowList.value.push(row[idKey.value]))
    } else {
      rowList.forEach(child => {
        nowExpandRowList.value.push(child[idKey.value])
      })
    }
  } else {
    nowExpandRowList.value = nowExpandRowList.value.filter(item => {
      const have = rowList.find(row => row[idKey.value] == item)
      if (have) {
        return false
      }
      return true
    })
  }
  removeSame(nowExpandRowList.value)
}

// 折叠全部
const setExpandAll = async (toExpand = true) => {
  nowExpandRowList.value = []
  if (toExpand) {
    setExpand(props.tableData, toExpand)
  }
}


// endregion


// region 自定义树形结构图标
const addMyExpandIcon = (data) => {
  nextTick(() => {
    if (props.treeIcon) {
      // 找到这个单元格
      const cellList = tableXV3Dom.value.querySelectorAll(`table.el-table__body tbody .el-table__expand-icon`)
      cellList.forEach(cell => {
        if (cell.children?.length == 1) {
          if (props.treeIcon && !cell.children[0].className.includes('myExpandIcon')) {
            // 重新挂载自己的元素
            const app = createApp({
              render() {
                return h(IconX, {
                  type: props.treeIcon,
                  size: 15,
                  style: {
                    transform: 'translateY(2px)',
                    // width: '14px',
                    // height: '14px',
                  },
                  class: 'xhAll myExpandIcon'
                });
              }
            })
            app.mount(cell)
          }
        }
      })
    }
  })

}
// endregion


// region 自定义勾选

// 当前勾选的列表。自定义勾选时用到列表，不在原有数据修改防止老是触发上面监听
const nowCheckKeyValue = ref({})

// region 表格label的全选插槽
// 勾选状态
const labelCheck = ref(false)
// 待选状态
const isIndeterminate = ref(false)

// 改变全部勾选事件
const labelCheckChangeFn = async ($event) => {
  for (let key in nowCheckKeyValue.value) {
    nowCheckKeyValue.value[key] = $event
  }
  getCheckValueFn()

  // 处理当前勾选的分页数据
  let thisCheckList = []
  if ($event) {
    // TODO: Warn(2024-11-25 9:57 by xhyk)：解决有禁止勾选过滤函数还会全部操作的bug
    for (let i = 0; i < levelTableData.value.length; i++) {
      const child = levelTableData.value[i]
      const result = await selectionFilterFn(child, i)
      if (result) {
        thisCheckList.push(child)
      }
    }
  }
  updateDataFn(thisCheckList)

  emit('selectX', {
    list: thisCheckList,
    row: undefined,
    toChoose: $event
  })
}

// endregion

// 初始化判断数据的勾选状态
const getCheckValueFn = () => {
  nextTick(async () => {
    // 判断是否出现全选
    let isAll = Object.keys(levelTableData.value).length > 0
    // nowCheckKeyValue：{idKey值: true}
    for (let idKeyValue in nowCheckKeyValue.value) {
      const haveChoose = nowCheckKeyValue.value[idKeyValue]
      // 没有选中的数据是在当前分页里面，才判断不是全部勾选
      if (haveChoose != true && idKeyValueList.value.indexOf(idKeyValue) != -1) {
        // TODO: Warn(2024-11-25 9:57 by xhyk)：解决有禁止勾选过滤函数还会全部操作的bug
        let $index = 0;
        const thisData = levelTableData.value.find((item, index) => {
          if (item[idKey.value] == idKeyValue) {
            $index = index;
            return true
          }
        })
        if (thisData) {
          const filterResult = await selectionFilterFn(thisData, $index)
          if (filterResult) {
            isAll = false
            break
          }
        } else {
          isAll = false
          break
        }
      }
    }

    isIndeterminate.value = false
    if (isAll) {
      labelCheck.value = true
    } else {
      // 不是全选，判断是否存在勾选数据
      labelCheck.value = false
      // 只判断当前分页的表格数据，来修改待勾选状态
      idKeyValueList.value.find((keyName) => {
        if (nowCheckKeyValue.value[keyName] == true) {
          isIndeterminate.value = true
          return true
        }
      })
    }

    // TODO: Warn(2024-11-25 9:57 by xhyk)：解决有禁止勾选过滤函数还会全部操作的bug
  })
}

// 单行勾选事件
const checkChangeFn = async ($event, key) => {
  nowCheckKeyValue.value[key] = $event
  // 改变勾选的显示
  getCheckValueFn()

  let thisRow = undefined
  // region 同步双向绑定
  // 当前页的勾选列表，不包含modelValue其他非当前页的数据
  let thisPageSelectList = levelTableData.value.filter(child => {
    if (child[idKey.value] == key) {
      thisRow = child
    }
    // 判断当前数据key是否勾选的
    let idKeyValue = child[idKey.value]
    return nowCheckKeyValue.value[idKeyValue] == true
  })
  updateDataFn(thisPageSelectList)

  // endregion


  emit('selectX', {
    list: thisPageSelectList,
    row: thisRow,
    toChoose: $event
  })

}


// endregion


// region 处理数据
const idKey = ref('tableV3Key')

// 设置唯一key对应的值
const setKeyValue = (item) => {
  let idList = []
  if (typeof props.rowKey == 'string') {
    idList = [props.rowKey]
  } else {
    // ['id','code'] => `${id}_${code}`
    idList = props.rowKey
  }

  let idValue = ""
  idList.forEach(key => {
    idValue = `${idValue}_${item[key]}`
  })
  item[idKey.value] = idValue

  return idValue
}

// 防止修改数据的key时被重新刷新
const setkeyTimeOut = new MyTimeOut(100, 100)

// idkey值列表
const idKeyValueList = ref([])

// 当前一层结构的数据
const levelTableData = ref([])

// 不直接使用props.tableData，是因为在处理idKey数据值时，有个空隙时间导致idKey的值为undefined导致唯一字段报错
const tableDataD = ref([])

// 是否可以进行监听modelValue了
const canWatchModelValue = ref(false)

// 处理唯一值。
watch(() => props.tableData, async (newV, oldV) => {
  nextTick(async () => {
    if (newV.length > 0) {
      // setkeyTimeOut.runBefore(async () => {
      idKeyValueList.value = []
      levelTableData.value = []
      canWatchModelValue.value = false

      if (props.isTree) {
        await findChildren(newV, (child, father) => {
          let childKeyValue = setKeyValue(child)
          idKeyValueList.value.push(childKeyValue)
          levelTableData.value.push(child)
        }, treeProps.value.children || 'children')
      } else {
        for (let i = 0; i < newV.length; i++) {
          const child = newV[i]
          let childKeyValue = setKeyValue(child)
          idKeyValueList.value.push(childKeyValue)
          levelTableData.value.push(child)
        }
      }

      // TODO: Finish(2024-11-27 16:13 by xhyk)：修复=props.tableData时，导致key更新不及时
      tableDataD.value = newV
      if (props.modelValue?.length > 0) {
        setCheckFnByModelValue()
      }
      canWatchModelValue.value = true
      // })
    } else {
      tableDataD.value = []
    }
  })
}, {immediate: true})


// 双向绑定的列表
const nowSelectList = ref([])

// 回显勾选
const setCheckFnByModelValue = () => {
  nowSelectList.value = props.modelValue || []
  lastModelValue.value = JSON.parse(JSON.stringify(nowSelectList.value))
  nowCheckKeyValue.value = {}
  levelTableData.value.forEach(row => {
    // 判断表格数据中是否存在勾选的数据
    let haveChoose = nowSelectList.value?.find(item => {
      let tempValue = setKeyValue(item)
      return tempValue == row[idKey.value]
    })
    if (haveChoose) {
      // 双向绑定列表中存在
      nowCheckKeyValue.value[haveChoose[idKey.value]] = true
      setChoose(haveChoose[idKey.value], 1, false, false, false)
    } else {
      // 不存在
      tableV3Dom.value && setChoose(row[idKey.value], 0, false, false, false)
    }
  })
  getCheckValueFn()
}

// 上一次勾选的列表
const lastModelValue = ref([])

watch(() => props.modelValue, (newV, oldV) => {
  const newList = JSON.parse(JSON.stringify(newV))
  // region 判断是否相等
  let isUpdate = false
  if (newList.length != lastModelValue.value.length) {
    isUpdate = true
  } else {
    // 数量相同
    newList.find((newItem) => {
      // 查找这个是不是不在旧列表中
      let idValue = setKeyValue(newItem)
      const have = lastModelValue.value.find((oldItem) => {
        let oldIdValue = setKeyValue(oldItem)
        if (idValue == oldIdValue) {
          return true
        }
      })
      // 不在旧列表中
      if (!have) {
        isUpdate = true
        return true
      }
    })
  }
  // endregion

  // 是更新而且不处于watch监听props.tableData的时候
  if (isUpdate && canWatchModelValue.value) {
    setCheckFnByModelValue()
  }
}, {deep: true})


// 同步双向绑定。addList：当前列表中的新增的数据，当前列表中没有在addList的就会被取消选择。
const updateDataFn = async (addlist = []) => {
  // 获取不是这个分页的当前勾选数据
  const newSelectList = nowSelectList.value.filter(nowItem => {
    return idKeyValueList.value.indexOf(nowItem[idKey.value]) == -1
  })
  // 添加新的列表
  newSelectList.push(...addlist)
  nowSelectList.value = newSelectList
  emit('update:modelValue', nowSelectList.value)
}


// endregion


// region 行点击事件

// 当前行对象
const nowRowData = ref({
  // tableData的当前行数据
  row: {},
  // 当单元格所在列
  column: {},
  // 事件
  event: {}
})

// 判断插槽是否进行触发行点击。
const cellClickEmitFn = (row, column, event) => {

  if (column == undefined) {
    // 不是插槽，没有rowClick赋值，默认是会触发点击事件
    emit("cellClick", {
      row,
      column,
      event
    })
    return true;
  }

  const rawColumnKey = column.rawColumnKey
  const thisTitle = tableTitleD.value.find((row, index) => {
    // 这个key对应上面html的<el-table-column>的循环的key属性值
    let thisRawColumnKey = `_${row.label}_${row.prop}_${row.slot}`
    return thisRawColumnKey == rawColumnKey
  })
  if (thisTitle) {
    if (thisTitle.slot == undefined) {
      // 不是插槽。判断是否需要进行触发行点击事件
      if (thisTitle.rowClick == false) {
        return false;
      } else {
        emit("cellClick", {
          row,
          column,
          event
        })
        return true
      }
    } else {
      // 插槽。判断是否需要进行触发行点击和单元格点击事件
      if (thisTitle.slotClick == true && thisTitle.rowClick != false) {
        emit("cellClick", {
          row,
          column,
          event
        })
        return true
      } else {
        return false
      }

    }
  }

  // 找不到item，没有rowClick赋值，默认是会触发点击事件
  emit("cellClick", {
    row,
    column,
    event
  })
  return true
}


// 行点击
const rowClickFn = (row, column, event) => {
  addCheckClass(row, column, event)
  let needSlotClick = cellClickEmitFn(row, column, event)
  if (!needSlotClick) {
    return;
  }
  emit('rowClick', {
    row,
    column,
    event
  })
  // 点击行时是否触发勾选事件
  setChoose(row, props.selectChange, true, false)

}

// 行双击
const rowClickDFn = (row, column, event) => {
  addCheckClass(row, column, event)
  let needSlotClick = cellClickEmitFn(row, column, event)
  if (!needSlotClick) {
    return;
  }
  emit('rowClickD', {
    row,
    column,
    event
  })
  // 点击行时是否触发勾选事件
  setChoose(row, props.selectChange, true, false)
}


// endregion


// region 行勾选


// 当个勾选事件
const selectFn = async (selectList = [], row = {}) => {
  if (haveMySelection.value) {
    return;
  }

  // 是勾选还是取消
  const isCheck = selectList.find(item => item[idKey.value] == row[idKey.value])
  await updateDataFn(selectList)
  emit("selectX", {
    list: selectList,
    row: row,
    toChoose: Boolean(isCheck)
  })

}

// 全部勾选事件
const selectAllFn = async (selectList, toEdit = true) => {
  if (haveMySelection.value) {
    return;
  }

  await updateDataFn(selectList)

  if (selectList.length > 0) {
    // 勾选字段或者自定义勾选字段
    const haveFilterFn = props.tableTitle.find(row => typeof row.selectable == 'function' &&
        (row.type == 'index' || row.type == 'selection'))

    // 解决在第二层父级勾选后，取消第三层子数据勾选，再全选后，第三层那个子数据没变化
    for (let i = 0; i < levelTableData.length; i++) {
      const child = levelTableData.value[i]
      // TODO: Warn(2024-11-25 9:57 by xhyk)：解决有禁止勾选过滤函数还会全部操作的bug
      if (haveFilterFn) {
        const result = await haveFilterFn.selectable(child, i)
        if (result) {
          tableV3Dom.value.toggleRowSelection(child, true)
        }
      } else {
        tableV3Dom.value.toggleRowSelection(child, true)
      }

    }
  }

  // 是勾选还是取消
  if (toEdit) {
    emit("selectX", {
      list: selectList,
      row: undefined,
      toChoose: selectList.length >= 1
    })
  }
}

// endregion


// region 处理行类名

// 给选中的行添加类名
const addCheckClass = (row, column, event) => {
  nowRowData.value = {
    row,
    column,
    event
  }
  return;
  nextTick(() => {
    let thisClass = []
    if (typeof props.checkClass == 'string') {
      thisClass = [props.checkClass]
    } else {
      thisClass = props.checkClass
    }

    // 去掉其他类名
    const dataRowDomList = document.querySelectorAll(`${props.fatherSelect || ''} .tableXV3 .tableXV3Content .el-table__row`)
    dataRowDomList.forEach(domItem => {
      thisClass.forEach(className => {
        domItem.classList.remove(className)
      })
    })

    // 给当前选中的行添加类名
    const thisCheckDom = document.querySelector(`${props.fatherSelect || ''} .tableXV3 .tableXV3Content .current-row`)
    if (thisCheckDom) {
      thisClass.forEach(className => {
        thisCheckDom.classList.add(className)
      })
    }
  })
}

// 行添加类名。注意不能用async
const addRowClassFn = (data) => {
  const queryRowClassName = getAttrFn('rowClassName')
  let queryName = ''
  if (typeof queryRowClassName == 'function') {
    queryName = queryRowClassName(data)
  } else {
    queryName = queryRowClassName
  }
  let result = `${queryName || ''} tableXV3RowClass`
  if (nowRowData.value.row[idKey.value] == data.row[idKey.value]) {
    let checkName = props.checkClass
    if (typeof props.checkClass == 'object') {
      checkName = props.checkClass.join(' ')
    }
    if (props.highlightCurrentRow) {
      result = `${result} ${checkName}`
    }
  }
  return result
}

// endregion


// region 点击头部，当前列
const nowColumnData = ref({
  // 当前点击列时事件
  event: {},
  // 表格head字段数据
  column: {}
})

const headerClickFn = (row, event) => {
  nowColumnData.value = {
    column: row,
    event: event
  }
  emit('headerClick', nowColumnData.value)
}

// endregion


// region 单元格，当选中的单元格，选中列的单元格样式

const nowCellData = ref({
  row: {},
  column: {},
  rowIndex: -1,
  columnIndex: -1
})

// 当前选中列的单元格类名
const columnColorName = ref('tableXV3_columnColor')

// 给单元格添加类名
const addCellClassFn = (data) => {
  const queryClassName = getAttrFn('cellClassName')
  let queryName = ''
  if (typeof queryClassName == 'function') {
    queryName = queryClassName(data)
  } else {
    queryName = queryClassName
  }

  let result = `${queryName || ''} tableXV3CellClass`
  const thisField = tableTitleD.value[data.columnIndex] || {}
  if (nowColumnData.value.column.id == data.column.id && thisField.columnClass != false) {
    let nowColumnClassName = props.nowColumnClass
    if (typeof nowColumnClassName == 'function') {
      nowColumnClassName = nowColumnClassName(nowColumnData.value.column, data.column)
    }
    result = `${result} ${nowColumnClassName} ${props.columnColor ? columnColorName.value : ''}`
  }

  addMyExpandIcon(data)
  return result

}

// endregion


// region 头部单元格样式，当选中列的头部单元格

const addHeadCClassFn = (data) => {
  const queryClassName = getAttrFn('headerCellClassName')
  let queryName = ''
  if (typeof queryClassName == 'function') {
    queryName = queryClassName(data)
  } else {
    queryName = queryClassName
  }

  let result = `${queryName || ''} tableXV3CellClass tableXV3Head`
  const thisField = tableTitleD.value[data.columnIndex] || {}
  if (nowColumnData.value.column.id == data.column.id && thisField.columnClass != false) {
    let nowColumnClassName = props.nowColumnClass
    if (typeof nowColumnClassName == 'function') {
      nowColumnClassName = nowColumnClassName(nowColumnData.value.column, data.column)
    }
    result = `${result} ${nowColumnClassName} ${props.columnColor ? columnColorName.value : ''}`
  }

  return result
}


// endregion

defineExpose({
  setChoose,
  setAllChoose,
  getChoose,
  setExpand,
  setExpandAll,
  // 当前自定义勾选列表
  nowCheckKeyValue,
  // 当前点击的列
  nowColumnData,
  // 当前点击的行
  nowRowData,
  // 当前单元格
  nowCellData,
})
</script>

<style scoped lang='scss'>
.tableXV3 {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: 0;

  .tableXV3Content {
    width: 100%;
    height: 100%;
  }
}


// region 滚动条样式
:deep(.tableXV3Content) {
  .el-table__body-wrapper {

    // region 横向滚动条
    .is-horizontal {
      //bottom: calc( - 2px);
      bottom: v-bind(scrollWidthX);
      transform: translateY(v-bind(scrollTranslateX));

      .el-scrollbar__thumb {
        height: v-bind(scrollWidthX) !important;
        background-color: v-bind(scrollColorX);
        opacity: v-bind(scrollOpacityX);
        display: v-bind(scrollDisplayX);
      }
    }

    // endregion

    // region 纵向滚动条
    .is-vertical {
      //right: calc($scrollW - 2px);
      right: v-bind(scrollWidthY);
      transform: translateX(v-bind(scrollTranslateY));
      z-index: 50;

      .el-scrollbar__thumb {
        width: v-bind(scrollWidthY) !important;
        background-color: v-bind(scrollColorY);
        opacity: v-bind(scrollOpacityY);
        display: v-bind(scrollDisplayY);
      }
    }

    // endregion

  }
}

// endregion


// region 鼠标经过
:deep(.hover-row) {
  &:hover {
    .el-table__cell {
      background-color: rgba(237, 245, 250, .8) !important;

    }
  }
}

// endregion


// region 当前选中行。.el-table__body tr.current-row>td.el-table__cell
$cv: #E2EDF8;
.tableXV3 {
  :deep(.tableXV3checkClass) {
    td.tableXV3CellClass.el-table__cell {
      border-top: 1px solid var(--menuSysColor, black) !important;
      border-bottom: 1px solid var(--menuSysColor, black) !important;
    }

    &:hover {
      //background-color: $cv;
    }
  }

  :deep(.hover-row) {
    &:hover {

    }
  }

}

// endregion

// region 勾选框大小
:deep(.el-table-column--selection) {
  .el-checkbox__inner {
    width: 16px;
    height: 16px;

    &:before {
      height: 6px;
    }

    &:after {
      left: 5px;
      top: 0px;
      height: 9px;
      width: 4px;
      border: 2px solid white;
      border-left: 0;
      border-top: 0;
      //transform: rotate(45deg) scaleY(1);
    }
  }
}

:deep(.myCheck) {
  .el-checkbox__inner {
    width: 16px;
    height: 16px;

    &:before {
      height: 6px;
    }

    &:after {
      left: 5px;
      top: 0px;
      height: 9px;
      width: 4px;
      border: 2px solid white;
      border-left: 0;
      border-top: 0;
      //transform: rotate(45deg) scaleY(1);
    }
  }
}

// endregion


.v3Label {
  display: inline-block;
}

.defaultLabel {
  display: flex;
  align-items: center;

  .labelHelpIcon {
    margin-left: 4px;
    display: flex;
    align-items: center;
    cursor: help;
    transform: translateY(-1px);
    color: var(--menuSysColor, #409EFF);
  }
}

// region 选中行/列样式

// 默认样式
.tableXV3 {
  :deep(.el-table__cell) {
    border-bottom: 1px solid #dddddd;
    border-right: 1px solid #ddd;
  }
}

// 默认的选中列样式
.tableXV3 {

  :deep(.tableXV3_columnColor.el-table__cell) {
    //background-color: var(--menuSysColor5, #D6E5FA) !important;
    background-color: #D8E5F7 !important;
    border-bottom: 1px solid #B1B3B7 !important;
    border-right: 1px solid var(--menuSysColor, black) !important;
    border-left: 1px solid var(--menuSysColor, black) !important;
  }

}

// endregion


// region 表头样式
.tableXV3 {
  :deep(.tableXV3Head) {
    //background-color: #F8F8F9 !important;
    background-color: #F0F0F0 !important;
    border-top: 1px solid #B1B3B7 !important;
    border-bottom: 1px solid #B1B3B7 !important;
  }
}

// endregion


// region 暂无数据进行居中
.tableXV3 {
  :deep(.el-scrollbar__wrap) {
    display: flex;

    .el-table__empty-text {
      font-size: 16px;
    }
  }
}

// endregion


</style>